Well, as I mentioned in comment, I made slightly modified code and that works really awesome. So I am going to elaborate what I did. Maybe it could help someone out.
Since I have to generate multiple sectioned Gridviews, I initially thought of generating tables using TableLayout and adding TableRows to it. Each Tablerow would be holding a RelativeView consiting of:
- Item's image (from JSON)
- Item's name (from JSON)
So, I initially created this code:
HomeFragment.java
public class HomeFragment extends Fragment {
.....
.....
ArrayList<String> itemList;
private JsonHelper jsonHelper;
private static final int GRID_COL_NUM = 3;
TableRow tbh;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container,false);
TableLayout table = (TableLayout) rootView.findViewById(R.id.tableLayout);
table.setStretchAllColumns(true);
table.setShrinkAllColumns(true);
try {
JSONObject obj = new JSONObject(loadJSONFromAsset("input.json"));
if(obj.has("catagories")) {
JSONArray catag = obj.getJSONArray("items");
for(int i=0; i< catag.length();i++){
JSONObject catitem = catag.getJSONObject(i);
JSONArray items = catitem.getJSONArray("catalog_items");
if(items.length() > 0) {
//Add a Grid Title for this category (span=6, full width)
TableRow tbr = addRowTitle(catitem.getString("cat_name"));
table.addView(tbr); //Add this title row to the table
itemList = new ArrayList<>(); //Hold all items for each category
for(int j=0;j<items.length();j++){
JSONObject singleItem = items.getJSONObject(j);
//I am trying to show only 3 items in a row, so add new row after 3
if(j % 3 == 0){
tbh = addRowGrid(); //add new row and repeat so after 3
table.addView(tbh); //Add this to the table, we will fill items below
}
LinearLayout lnr = new LinearLayout(getActivity());
TableRow.LayoutParams params = new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT,TableRow.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER; //Center the items
lnr.setLayoutParams(params);
lnr.setOrientation(LinearLayout.VERTICAL); //As per your need. worked for me
//Item's Image
ImageView itemImg = new ImageView(getActivity()); itemImg.setImageResource(R.drawable.ic_communities); //Default icon for item
TextView itemLabel = new TextView(getActivity());
itemLabel.setText(singleItem.getString("name"));
itemLabel.setTypeface(Typeface.SERIF, Typeface.BOLD);
lnr.addView(postImg,params);
lnr.addView(postLabel,params);
tbh.addView(lnr); //Now we have image and content, wrap in linear layout and add to the row.
itemList.add(singleItem.getString("name")); //This step is not needed for now. I just wrote this to use this array for other purposes.
}
//I am all confused at this point if I have really closed all of curly braces. Please confirm
}
}
}
} catch (JSONException e){
e.printStackTrace();
}
return rootView;
}
public TableRow addRowGrid()
{
return new TableRow(getActivity());
}
public TableRow addRowTitle(String titleb)
{
TableRow rowTitle = new TableRow(getActivity());
rowTitle.setGravity(Gravity.CENTER_HORIZONTAL);
// title column/row
TextView title = new TextView(getActivity());
title.setText(titleb);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
title.setGravity(Gravity.CENTER);
title.setTypeface(Typeface.SERIF, Typeface.BOLD);
TableRow.LayoutParams params = new TableRow.LayoutParams();
params.span = 6;
rowTitle.addView(title, params);
return rowTitle;
}
...
...
}
This whole code works for a single Table layout defined in layout:
fragment_home:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/card_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true">
<RelativeLayout
android:id="@+id/relativeLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TableLayout
android:id="@+id/tableLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"></TableLayout>
</RelativeLayout>
</ScrollView>
So the whole table is generated on the fly. Worked pretty well. I can attach a click handler to each row to listen, and I might have to set tag for each views to get the clicked item. Its then I thought to more feasible and flexible approach. What if I add the gridview to each row itself, instead of appending LinearLayouts to each TableRows? So, I ended up on modifying my code on this approach:
1. A single TableRow will hold each category title and a second Tablerow will be appended to it, which will hold the gridview.
2. I can now use a Custom Grid Adapter by which I could change layout of each item better, i.e via XML instead of tingling here "on-the-fly" approach.
3. Above point also gained me a clicklistener. So no setting of tag required.
NOTE: The above approach was working as well as below approach is working too (as same). So you can use both
Hence, I create a function that would add title to each category of item and a function to add items to grid, and grid to a row:
HomeFragment.java (gridview version)
public class HomeFragment extends Fragment {
private static final int GRID_COL_NUM = 3;
TableRow tbh;
public HomeFragment(){}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
TableLayout table = (TableLayout) rootView.findViewById(R.id.tableLayout);
table.setStretchAllColumns(true);
table.setShrinkAllColumns(true);
try {
JSONObject obj = new JSONObject(loadJSONFromAsset("input.json"));
if(obj.has("catagories")) {
JSONArray catag = obj.getJSONArray("catagories");
for(int i=0; i< catag.length();i++) {
JSONObject catitem = catag.getJSONObject(i);
if (catitem.has("items")) {
JSONArray items = catitem.getJSONArray("catalog_items");
if (items.length() > 0) {
//Add row with title catname
TableRow tbr = addRowTitle(catitem.getString("cat_name")); //Add category Name as title
table.addView(tbr); //Same I did previously
ArrayList<String> itemList = new ArrayList<>();
for (int j = 0; j < items.length(); j++) {
JSONObject singleItem = posts.getJSONObject(j);
itemList.add(singleItem.getString("name"));
}
tbh = addGridRow(itemList); //Create Grid of collections and add it to a tablerow
table.addView(tbh); //Add this tablerow to table
}
}
}
}
if(obj.has("pages")) {
JSONArray pages = obj.getJSONArray("pages");
if(pages.length() > 0) {
TableRow tbr = addRowTitle("Pages");
table.addView(tbr);
ArrayList<String> pageList = new ArrayList<>();
for (int i = 0; i < pages.length(); i++) {
JSONObject page = pages.getJSONObject(i);
pageList.add(page.getString("name"));
}
tbh = addGridRow(pageList);
table.addView(tbh);
}
}
} catch (JSONException e){
e.printStackTrace();
}
return rootView;
}
/**
* Now this is a life saver. Gridviews aren't friendly with scrollviews (I might be wrong, but happens with me everytime), especially if generated dynamically. Hence this function calculates height of each item of gridview dynamically and hence sets the total view height adapted from https://stackoverflow.com/a/22555947/1136491
**/
public void setGridViewHeightBasedOnChildren(GridView gridView, int columns) {
ListAdapter listAdapter = gridView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
int items = listAdapter.getCount();
int rows = 0;
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);
}
public TableRow addGridRow(ArrayList gridItemList)
{
TableRow gridrow = new TableRow(getActivity());
GridView gv = new GridView(getActivity());
TableRow.LayoutParams params = new TableRow.LayoutParams(
TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
gv.setLayoutParams(params);
gv.setNumColumns(GRID_COL_NUM); //3 columns grid
gv.setAdapter(new HomeGridAdapter(getActivity(), gridItemList)); //Custom grid adapter I was talking about. See Below
gridrow.addView(gv);
//Thanks for not messing up my gridview inside scrollview
setGridViewHeightBasedOnChildren(gv,GRID_COL_NUM); //3 Columns
return gridrow;
}
//Simply add title above the gridview
public TableRow addRowTitle(String titleb)
{
TableRow rowTitle = new TableRow(getActivity());
rowTitle.setGravity(Gravity.CENTER_HORIZONTAL);
// title column/row
TextView title = new TextView(getActivity());
title.setText(titleb);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
title.setGravity(Gravity.CENTER);
title.setTypeface(Typeface.SERIF, Typeface.BOLD);
TableRow.LayoutParams params = new TableRow.LayoutParams();
params.span = 6;
rowTitle.addView(title, params);
return rowTitle;
}
....
}
Now my custom Adapter
HomeGridAdapter.java
public class HomeGridAdapter extends BaseAdapter {
ArrayList<String> result;
Context context;
private static LayoutInflater inflater=null;
public HomeGridAdapter(Context context, ArrayList<String> itemList) {
// TODO Auto-generated constructor stub
this.result=itemList;
this.context=context;
inflater = ( LayoutInflater )context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return result.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
public class Holder
{
TextView tv;
ImageView img;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
Holder holder=new Holder();
View rowView;
//Layout mentioned below
rowView = inflater.inflate(R.layout.home_grid_layout, null);
holder.tv=(TextView) rowView.findViewById(R.id.textView1);
holder.img=(ImageView) rowView.findViewById(R.id.imageView1);
//Item's Text
holder.tv.setText(result.get(position));
//Item's Image
holder.img.setImageResource(R.drawable.ic_launcher);
//A click listener to each gid item
rowView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(context, "You Clicked "+result.get(position), Toast.LENGTH_SHORT).show();
}
});
return rowView;
}
}
home_grid_layout.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="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView1"
android:layout_gravity="center"
android:layout_width="88dp"
android:layout_height="88dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/textView1"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:text="TextView" />
</LinearLayout>
So I am currently using Gridview version of the TableLayout. I might have achieved what I wanted, but I would really welcome any suggestions here. Hope this answer serves someone a great help.
Thanks for bearing this long.