Visible slowness when calling requestLayout - android

I have a Dialogfragment which contains TableLayout. I have wrapped the TableLayout with a custom class (given below) because i wanted a fixed header and a scrolling body. And I wanted the header to align properly with the body. The custom class does this by overriding onLayout.
<com.ui.components.ScrollingTable
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:layout_weight="1"
android:background="#color/transaction_table_bg">
<TableLayout
android:id="#+id/tlScrollingTableHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"
android:fillViewport="true">
<TableLayout
android:id="#+id/tlScrollingTableBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</ScrollView>
</com.ui.components.ScrollingTable>
I used the following class to wrap the TableLayout.
public class ScrollingTable extends LinearLayout {
private static final String TAG = ScrollingTable.class.getSimpleName();
public ScrollingTable (Context context) {
super(context);
}
public ScrollingTable (Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
List<Integer> colWidths = new LinkedList<Integer>();
TableLayout header = (TableLayout) findViewById(R.id.tlScrollingTableHeader);
TableLayout body = (TableLayout) findViewById(R.id.tlScrollingTableBody);
// Measure content width first
for (int rownum = 0; rownum < body.getChildCount(); rownum++) {
TableRow row = (TableRow) body.getChildAt(rownum);
int countCells = 0;
for (int cellnum = 0; cellnum < row.getChildCount(); cellnum++) {
View cell = row.getChildAt(cellnum);
if (cell.getVisibility() == VISIBLE) {
Integer cellWidth = cell.getWidth();
if (colWidths.size() <= countCells) {
colWidths.add(cellWidth);
} else {
Integer current = colWidths.get(countCells);
if (cellWidth > current) {
colWidths.remove(countCells);
colWidths.add(countCells, cellWidth);
}
}
countCells++;
}
}
}
// Figure out if header needs resizing first based on widths
TableRow headerRow = (TableRow) header.getChildAt(0);
for (int count = 0; count < colWidths.size(); count++) {
if (headerRow.getChildAt(count).getWidth() >= colWidths.get(count)) {
colWidths.remove(count);
colWidths.add(count, headerRow.getChildAt(count).getWidth());
}
}
// Then apply to header
for (int rownum = 0; rownum < header.getChildCount(); rownum++) {
TableRow row = (TableRow) header.getChildAt(rownum);
for (int cellnum = 0; cellnum < row.getChildCount(); cellnum++) {
View cell = row.getChildAt(cellnum);
TableRow.LayoutParams params = (TableRow.LayoutParams) cell.getLayoutParams();
params.width = colWidths.get(cellnum);
cell.requestLayout();
}
}
}
}
This works well except for one problem. When the Dialog opens I can clearly see the columns resizing due to requestLayout. Is there anyway to fix this ?

As Nguyen suggested I moved to another thread. However I removed requestLayout. Instead in the new thread I used the following which seems to do the trick.
#Override
protected void onPostExecute(Void result) {
tableHeader.setColumnCollapsed(0, false);
tableContent.setColumnCollapsed(0, false);
}

Related

Graphical representation of the current value into the total value

Suppose I have a total value of 2000, while my current value is 180. I would like to present it graphically as follows:
How could I do that? I was looking for many charts, but I did not find such a solution anywhere.
Is there any open source that provides such graphical representation of values?
You can use TableLayout to achieve this.
Your layout will look like this:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
android:id="#+id/tableLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</TableLayout>
Then in your code you could populate your table like so:
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int totalValue = 2000;
int currentValue = 180;
int rowsNumber = 10;
int columnsNumber = 10;
TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
for (int i = 0; i < rowsNumber ; i++) {
TableRow tableRow = new TableRow(this);
tableRow.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
tableRow.setBackgroundResource(R.drawable.yourRowDrawable);
for (int j = 0; j < columnsNumber; j++) {
ImageView imageView = new ImageView(this);
imageView.setImageResource(this.getCellDrawableId(i,j,totalValue ,currentValue ));
tableRow.addView(imageView, j);
}
tableLayout.addView(tableRow, i);
}
}
}
private int getCellDrawableId(int i, int j,int totalValue ,int currentValue ){
if(/*some logic here*/)
return R.drawable.greenCell;
return R.drawable.emptyGrayCell;
}
P.S.: Where R.drawable.greenCell, R.drawable.emptyGrayCell, R.drawable.yourRowDrawable are appropriate drawable to represent your grid.
Also I did not test this code, only writeen it here like snippet, so some errors may be present here

How to get First Visible child item of android Horizontal scrollview on scrollChange event

I need to find the first visible child item of android Horizontal scrollview on scrollChange event. I have followed this link How to get First Visible child item of android scrollview on scrollChange event
but couldn't find what rowView is.
horizontalScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollPos = horizontalScrollView1.getScrollX();
Log.e("scroll x:", scrollPos + "");
}
});
layout file
<?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="match_parent">
<HorizontalScrollView
android:id="#+id/horizontalScrollView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="15dp"
android:fillViewport="true"
android:scrollbars="none">
<RelativeLayout
android:id="#+id/layLandmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<View
android:id="#+id/viewWhite"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_centerInParent="true"
android:background="#color/white" />
</RelativeLayout>
</HorizontalScrollView>
</RelativeLayout>
Class file:
ImageView iv;
RelativeLayout layLandmarks = (RelativeLayout) findViewById(R.id.layLandmarks);
FrameLayout.LayoutParams lp1 = new FrameLayout.LayoutParams(2000, FrameLayout.LayoutParams.WRAP_CONTENT);
layLandmarks.setLayoutParams(lp1);
JSONObject landmarks = jsonObject1.getString("landmarks");
JSONArray jsonArray1 = new JSONArray(landmarks);
for (int j = 0; j < jsonArray1.length(); j++) {
JSONObject jsonObject2 = jsonArray1.getJSONObject(0);
landmarkId = jsonObject2.getString("id");
landmarkName = jsonObject2.getString("title");
landmarkDesc = jsonObject2.getString("description");
latitude = jsonObject2.getDouble("latitude");
longitude = jsonObject2.getDouble("longitude");
video_time = jsonObject2.getString("video_time_in_sec");
// Log.e("video_time_in_sec", video_time);
landmarkIcon = jsonObject2.getString("image");
iv = new ImageView(VideoPreviewWithRecycler.this);
iv.setId(i);
Picasso
.with(VideoPreviewWithRecycler.this)
.load(landmarkIcon)
.resize(40, 45)
.into(iv);
params = new RelativeLayout.LayoutParams(40, 45);
params.topMargin = 0;
params.leftMargin = Integer.parseInt(video_time);
params.addRule(RelativeLayout.RIGHT_OF, 0);
layLandmarks.addView(iv, params);
}
Update:
horizontalScrollView1.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, final int scrollX, int scrollY, final int oldScrollX, int oldScrollY) {
horizontalScrollView1.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int firstVisibleItemIndex= getFirstVisibleItem1(horizontalScrollView1);
Log.e("firstVisibleItemIndex >>",firstVisibleItemIndex+"");
}
});
}
});
private int getFirstVisibleItem1(HorizontalScrollView scrollView) {
ViewGroup viewGroup = (ViewGroup) scrollView.getChildAt(0); // cast to ViewGroup here because I think we only need to getFirstVisibleItem if ScrollView have multiple items
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View view = viewGroup.getChildAt(i);
if (view.getX() + view.getWidth() >= scrollView.getScrollX()) {
return i; // if you want to receive the position, return i here
}
}
return 0;
}
I think it may help
private View getFirstVisibleItem(HorizontalScrollView scrollView) {
ViewGroup viewGroup = (ViewGroup) scrollView.getChildAt(0); // cast to ViewGroup here because I think we only need to getFirstVisibleItem if ScrollView have multiple items
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View view = viewGroup.getChildAt(i);
if (view.getX() + view.getWidth() >= scrollView.getScrollX()) {
return view; // if you want to receive the position, return turn i here
}
}
return null;
}
The idea is check each view x position and scroll x value
I implemented some basic scrolling animation in my app. I used this piece of code and it is working perfectly fine.
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
case RecyclerView.SCROLL_STATE_DRAGGING:
case RecyclerView.SCROLL_STATE_SETTLING:
currentItemPosition=linearLayoutManager.findFirstCompletelyVisibleItemPosition();
}
}
my requirement was to find the position during settling of scroll ,you can try the same during SCROLL_STATE_IDLE
There are few methods but I prefer only one.
All Views have "boolean View.getGlobalVisibleRect(..)" ( https://developer.android.com/reference/android/view/View.html#getGlobalVisibleRect(android.graphics.Rect) ) method that returns FALSE if the View is completly hidden by its parent.
So all HorizontalScrollView's child has that HScrollView as its parent and if that method returns FALSE it means that View is not visible. The first child returning TRUE is the first visible child inside that HScrollView.
You can use Linearlayout Instead of RelaytiveLayout as a child of HorizontalScrollView. Like this:
<?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="wrap_content">
<HorizontalScrollView
android:id="#+id/horizontalScrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<LinearLayout
android:id="#+id/layLandmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp" />
</HorizontalScrollView>
</RelativeLayout>
Note: You can also use custom layout to inflate in views in LinearLayout for more performance in case of nested Layouts. (I used a TextView not Custom Layout here)
In your activity use something like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final HorizontalScrollView horizontalScrollView = findViewById(R.id.horizontalScrollView);
TextView iv;
final LinearLayout layLandmarks = findViewById(R.id.layLandmarks);
for (int j = 0; j < 10; j++) {
iv = new TextView(this);
iv.setId(j);
iv.setText("TEXT " + j);
iv.setPadding(20, 20, 20, 20);
layLandmarks.addView(iv);
}
horizontalScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int firstVisibleItemIndex = getFirstVisibleItem(layLandmarks, horizontalScrollView);
Log.e("ItemIndex >>", firstVisibleItemIndex + "");
}
});
}
Now Use the code below. It modified version of #Phan Van Linh Answer.
private int getFirstVisibleItem(LinearLayout linearLayout, HorizontalScrollView horizontalScrollView) {
for (int i = 0; i < linearLayout.getChildCount(); i++) {
View view = linearLayout.getChildAt(i);
if (view.getX() + view.getWidth() >= horizontalScrollView.getScrollX()) {
return i; // if you want to receive the position, return i here
}
}
return 0;
}
I Hope this will solve your problem. :)

ExpandableListView dynamic child height

I have a ExpandableListView which is inside an LinearLayout as container and set by using CustomAdapter.
For its children, I'm using onGroupClick() method to send an request to specific service and getting result as String, then filling child list of clicked group item.
The Problem is since I can't get the updated height (after service response has set to text view of child view's text view) the linearlayout container height doesn't increase the way it should. And it also creates a scrolling problem.
Though list child item xml is WRAP_CONTENT as below:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="#dimen/rowHeight"
android:background="#color/colorLightYellow">
<TextView
android:id="#+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="#dimen/marginGeneral"
android:layout_toLeftOf="#+id/tvCount"
android:gravity="center_vertical"
android:paddingBottom="#dimen/marginGeneral"
android:paddingTop="#dimen/marginGeneral"
android:text="tvTitle"
android:textColor="#color/colorGray"
android:textSize="#dimen/fontSizeNormal" />
...
</RelativeLayout>
So the code part is little long stay with me:
#Override
public boolean onGroupClick(ExpandableListView parent, View v, final int groupPosition, long id) {
Map<Item, List<ItemDetail>> childList = detailExpandableAdapter.getChildList();
final Item item = detailExpandableAdapter.getGroup(groupPosition);
if (childList.get(item).size() == 0) {
startProgressDialog();
GlobalApplication.getService().getItemDetails(Session.getCurrent().getSessionId(), getItem.item.itemNo, item.name, new ServiceCallback<GetItemDetails>() {
#Override
public void onSuccess(GetItemDetails response) {
stopProgressDialog();
List<ItemDetail> itemDetailList = null;
if (GetItemDetails.isSuccess(response)) {
itemDetailList = response.getItemDetailList();
} else {
itemDetail itemDetail = new ItemDetail();
itemDetail.resultDesc = response.getResult().getResultDesc();
if (StringUtils.isNullOrWhitespace(itemDetail.resultDesc)) {
itemDetail.resultDesc = Result.getGeneralFailResult().getResultDesc();
}
itemDetailList = new ArrayList<ItemDetail>();
itemDetailList.add(itemDetail);
}
if (itemDetailList != null) {
Map<Item, List<ItemDetail>> childList = detailExpandableAdapter.getChildList();
if (childList.containsKey(item)) {
childList.remove(item);
}
childList.put(item, itemDetailList);
detailExpandableAdapter.setChildList(childList);
detailExpandableAdapter.notifyDataSetChanged();
detailExpandableAdapter.notifyDataSetInvalidated();
listViewLastItems.expandGroup(groupPosition);
}
}
#Override
public void onFail() {
stopProgressDialog();
}
});
return false;
}
return false;
}
#Override
public void onGroupExpand(int groupPosition) {
setExpandableListViewHeightStable(listViewLastItems, llListViewItemDetailContainer);
if (lastExpanded != -1 && groupPosition != lastExpanded)
listViewItems.collapseGroup(lastExpanded);
lastExpanded = groupPosition;
}
public void setExpandableListViewHeight(ExpandableListView expandableListView, LinearLayout linearLayoutParent){
try {
ExpandableListAdapter expandableListAdapter = expandableListView.getExpandableListAdapter();
int totalHeight = 0;
int desiredWidth = View.MeasureSpec.makeMeasureSpec(expandableListView.getWidth(), View.MeasureSpec.UNSPECIFIED);
for (int i = 0; i < expandableListAdapter.getGroupCount(); i++) {
View groupItem = expandableListAdapter.getGroupView(i, false, null, expandableListView);
groupItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
//Logger.debug("recalculateExpandableListViewHeight listItem:"+groupItem.getMeasuredHeight());
totalHeight += groupItem.getMeasuredHeight();
if (expandableListView.isGroupExpanded(i)){
for (int j = 0; j < expandableListAdapter.getChildrenCount(i); j++) {
View listItemChild = expandableListAdapter.getChildView(i, j, false, null, expandableListView);
listItemChild.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, View.MeasureSpec.UNSPECIFIED));
listItemChild.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
Logger.debug("recalculateExpandableListViewHeight listItemChild:" + listItemChild.getMeasuredHeight());
totalHeight += listItemChild.getMeasuredHeight();
}
}
}
linearLayoutParent.getLayoutParams().height = totalHeight + (expandableListAdapter.getGroupCount() * expandableListView.getDividerHeight());
linearLayoutParent.requestLayout();
} catch (Exception e) {
Logger.printStackTrace(e);
}
}
Update: this is the linear layout I use as container
<LinearLayout
android:id="#+id/llListViewItemContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/tvItemDueDate"
android:layout_marginTop="#dimen/marginGeneral"
android:orientation="vertical"/>
Update 2: I'm adding ExpandableListView to LinearLayout dynamically.
listViewItems = new ExpandableListView(getContext());
listViewItems.setScrollContainer(false);
listViewItems.setDivider(new ColorDrawable(getResources().getColor(R.color.colorLightGray)));
listViewItems.setDividerHeight(UIHelper.convertDptoPixels(1));
listViewItems.setGroupIndicator(null);
listViewItems.setOnGroupClickListener(this);
listViewItems.setOnGroupExpandListener(this);
listViewItems.setOnGroupCollapseListener(this);
//generate empty child list
Map<Item, List<ItemDetail>> childMap = new HashMap<>();
for (Item item : getItems.getItemList()) {
childMap.put(item, new ArrayList<ItemDetail>());
}
detailExpandableAdapter = new detailExpandableAdapter(getActivity(), getItems.getItemList(), childMap);
listViewItems.setAdapter(detailExpandableAdapterF);
listViewItems.removeAllViews();
listViewItems.addView(listViewLastItems);
UIHelper.setExpandableListViewHeightStable(listViewItems, llListViewDetailContainer);
Use below given custom ExpandableListView class and override onMeasure method.
public class MyExpandableListView extends ExpandableListView {
public MyExpandableListView(Context context) {
super(context);
}
public MyExpandableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyExpandableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
And use it like,
<com.app.custom.NonScrollExpandableListView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Replace com.app.custom with your package name in which you put this custom class.
If possible use NestedScrollView instead of ScrollView, as it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.
Let me know if this help you or not. Happy Coding!!!
I would suggest you to use HeaderView property of ExpandableListView. AS ExpandableListView is a derived class of ListView so HeaderView property must be there as I believe.
Issues in using ListView inside ScrollView -
Performance - If you going to play with measure property of ListView then it will surely affect recycling of cells.
User Experience - Strange behaviour comes when ListView's parent is another scrollable view.
Better move your LinearLayout stuff inside another view, then inflate that view and either put it in Header or Footer of ListView as per your need.
// Inflated View which is going to be use as Header of view
ViewGroup headerView = (ViewGroup)getLayoutInflater().inflate(R.layout.list_header,expListView,false);
// Add that view to Header
your_expandale_listView.addHeaderView(headerView);

ScrollView with multiple LinearLayouts doesn't size correctly

I have created a GridView control, which inhertis from a ScrollView, the idea of this control, is that it will contain multiple Views arranged in a grid format with a given number of columns and rows.
When the view is first built, the GridView doesn't know the size of its container, so I wait until the onSizeChanged method is called, then I apply the relevant sizing.
When the below is run, it doesn't re-size the grid to show it correctly, each control is only as big as it needs to be to show the text.
When the `onSizeChanged' method is called, it has the correct size, and applies the correct size to each child view, but it doesn't affect the way the controls are drawn (i.e. they're still all bunched up on the top left of the screen).
Despite this, I have actually got it working, but it draws twice. I do this by creating a Runnable which calls ResizeList. Then calling new Handler().post(r) straight after I call BuildIt.
Although this is a solution, I just don't understand why it doesn't work in the below form.
Incidentally, if the GridView is the main View added to the Activity, it displays fine, it's only when it's subsequently added. Which is why I have the Button, which you have to press to show the grid.
Can anyone suggest why the below code doesn't work properly?
public class MainActivity extends Activity {
GridView sv;
FrameLayout flay;
#Override protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
flay=new FrameLayout(this);
this.setContentView(flay);
Button b=new Button(this);
b.setText("press me to show grid view");
b.setOnClickListener(ocl);
flay.addView(b);
}
OnClickListener ocl=new OnClickListener()
{
#Override public void onClick(View v)
{
BuildIt();
}};
private void BuildIt()
{
flay.removeAllViews(); // remove the button control
sv=new GridView(this);
for (int c1=0 ; c1<30 ; c1++)
{
TextView tv=new TextView(this);
tv.setText("Item "+c1);
tv.setGravity(android.view.Gravity.CENTER);
sv.addListItem(tv);
}
flay.addView(sv);
sv.ConstructList();
}
}
The GridView class
public class GridView extends ScrollView
{
final int rows=4;
final int cols=4;
private ArrayList<View> allViews=new ArrayList<View>();
private LinearLayout ll;
public GridView(Context context)
{
super(context);
ll=new LinearLayout(context);
ll.setOrientation(LinearLayout.VERTICAL);
this.addView(ll);
}
public void addListItem(View v)
{
allViews.add(v);
}
public void ConstructList()
{
int c1=0;
ll.removeAllViews(); // Just in case we're re-building
LinearLayout row=null;
for (View v : allViews)
{
if (c1%cols==0)
{
row=new LinearLayout(this.getContext());
ll.addView(row);
}
row.addView(v);
c1++;
}
}
private void ResizeList()
{
int useHeight=getHeight()/rows;
int useWidth=getWidth()/cols;
LinearLayout.LayoutParams lpCol=new LinearLayout.LayoutParams(useWidth, useHeight);
Log.i("log","About to set width/height="+useWidth+"/"+useHeight);
int numKids= ll.getChildCount();
for (int c1=0 ; c1<numKids ; c1++)
{
LinearLayout ll2=(LinearLayout)ll.getChildAt(c1);
for (int c2=0 ; c2<ll2.getChildCount() ; c2++) // use getChildCount rather than cols, just in case it's the last one
{
View v=ll2.getChildAt(c2);
v.setLayoutParams(lpCol);
}
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
ResizeList();
}
}
I have a function which is used to resize the child's width and height in gridView.
May be this could help you :
public static void setGridChild_Height(GridView gridView, int columns) {
ListAdapter listAdapter = gridView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
int items = listAdapter.getCount();
int rows = 0;
for (int j = 0; j < items; j++) {
View view = gridView.getChildAt(j);
if (view != null && view.getHeight() > totalHeight) {
totalHeight = view.getHeight();
}
}
System.out.println("totalHeight -> " + totalHeight);
if (totalHeight > 0) {
for (int j = 0; j < items; j++) {
View view = gridView.getChildAt(j);
if (view != null && view.getHeight() < totalHeight) {
view.setMinimumHeight(totalHeight);
}
}
}
// View listItem = listAdapter.getView(0, null, gridView);
// listItem.measure(0, 0);
// totalHeight = listItem.getMeasuredHeight();
//
// float x = 1;
// if (items > columns) {
// x = items / columns;
// rows = (int) (x + 1);
// totalHeight *= rows;
// }
//
// ViewGroup.LayoutParams params = gridView.getLayoutParams();
// params.height = totalHeight;
// gridView.setLayoutParams(params);
}
Try to change logic as per your requirement.
Code is not tested perfectly.
It's because onSizeChanged when newly added to the view hierarchy uses it's old sizes of "0" (according to the docs: http://developer.android.com/reference/android/view/View.html#onSizeChanged(int, int, int, int))
I think what you want is to a addOnLayoutChangedListener : http://developer.android.com/reference/android/view/View.html#addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener)
Using the ViewTreeObserver might be another option that will work for you: How can you tell when a layout has been drawn?

Android which layout to use to overflow objects to the next line

I am uncertain of what type of layout to use for this certain scenario.
I basically want to have a horizontal linear layout that i can add views to. in this case buttons (displaying tags in an application) But each view will have a different width bases on the name of the tag it is displaying, so i want to add say 10 tags, I need a layout that will fit as many as it can on the 1st line, and then if it doesn't fit, automatically overflow to the next line.
Basically how a text view works with text, if the text is longer than the width it goes to the next line, except I want to do this with non-clickable buttons.
I thought of a grid layout, but then it would have the same no of "tags" on each line when you could have 2 tags with a long name on the first line and 7 with a short name on the second line.
Something that looks a bit like this:
I basically want the look of how stack overflow does it below here.
Answer: Your own custom Layout :)
I know this is a late answer to this question. But it might help the OP or someone for sure.
You can extend ViewGroup to create a custom layout like this one below. The advantage of this is you get the keep the view hierarchy flat.
MyFlowLayout
public class MyFlowLayout extends ViewGroup {
public MyFlowLayout(Context context) {
super(context);
}
public MyFlowLayout(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public MyFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int realWidth = MeasureSpec.getSize(widthMeasureSpec);
int currentHeight = 0;
int currentWidth = 0;
int currentChildHookPointx = 0;
int currentChildHookPointy = 0;
int childCount = this.getChildCount();
for(int i = 0; i < childCount; i++) {
View child = getChildAt(i);
this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
//check if child can be placed in the current row, else go to next line
if(currentChildHookPointx + childWidth > realWidth) {
//new line
currentWidth = Math.max(currentWidth, currentChildHookPointx);
//reset for new line
currentChildHookPointx = 0;
currentChildHookPointy += childHeight;
}
int nextChildHookPointx;
int nextChildHookPointy;
nextChildHookPointx = currentChildHookPointx + childWidth;
nextChildHookPointy = currentChildHookPointy;
currentHeight = Math.max(currentHeight, currentChildHookPointy + childHeight);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.x = currentChildHookPointx;
lp.y = currentChildHookPointy;
currentChildHookPointx = nextChildHookPointx;
currentChildHookPointy = nextChildHookPointy;
}
currentWidth = Math.max(currentChildHookPointx, currentWidth);
setMeasuredDimension(resolveSize(currentWidth, widthMeasureSpec),
resolveSize(currentHeight, heightMeasureSpec));
}
#Override
protected void onLayout(boolean b, int left, int top, int right, int bottom) {
//call layout on children
int childCount = this.getChildCount();
for(int i = 0; i < childCount; i++) {
View child = this.getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
}
}
#Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MyFlowLayout.LayoutParams(getContext(), attrs);
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new MyFlowLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
#Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new MyFlowLayout.LayoutParams(p);
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof MyFlowLayout.LayoutParams;
}
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
int spacing = -1;
int x = 0;
int y = 0;
LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray t = c.obtainStyledAttributes(attrs, R.styleable.FlowLayout_Layout);
spacing = t.getDimensionPixelSize(R.styleable.FlowLayout_Layout_layout_space, 0);
t.recycle();
}
LayoutParams(int width, int height) {
super(width, height);
spacing = 0;
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
}
Usage in a layout.xml file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:cl="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.merryapps.customlayout.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<com.merryapps.customlayout.MyFlowLayout
android:id="#+id/flw1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FF0000">
<Button
android:id="#+id/b1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello"
cl:layout_space="20dp"/>
<Button
android:id="#+id/b2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"/>
<Button
android:id="#+id/b4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"/>
<Button
android:id="#+id/b5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"/>
<Button
android:id="#+id/b6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"/>
</com.merryapps.customlayout.MyFlowLayout>
<Button
android:id="#+id/b3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"
android:textAllCaps="false"/>
</LinearLayout>
For show type of view one must go for flow layout :-
There are many libraries available on Git
Following is the example of,
blazsolar/FlowLayout
Add this line in app.gradle
compile "com.wefika:flowlayout:<version>"
Usage:-
<com.wefika.flowlayout.FlowLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start|top">
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Lorem ipsum" />
</com.wefika.flowlayout.FlowLayout>
For detail implementation follow below link-
https://github.com/blazsolar/FlowLayout
You can try this links too:-
https://github.com/xiaofeng-han/AndroidLibs/tree/master/flowlayoutmanager (try this)
https://github.com/ApmeM/android-flowlayout
https://gist.github.com/hzqtc/7940858

Categories

Resources