动态生成多个Gridview

Generating multiple Gridviews dynamically

我正在从 API 中获取一些内容,我需要在 android 应用程序中显示这些内容。内容采用 JSON 格式,看起来像这样:

{
  "items": [
    {
      "catalog_items": [
        {
          "date": "23-01-2015", 
          "content": "Trimmer 1 description", 
          "name": "Trimmer 1"
        }, 
        {
          "date": "25-01-2015", 
          "content": "Trimmer 2 description", 
          "name": "Trimmer 2"
        }
        .....
        .....
      ], 
      "item_category": "Trimmer"
    }, 
    {
      "catalog_items": [
        {
          "date": "13-08-2014", 
          "content": "Shirt description here", 
          "name": "John Player Shirt"
        }
      ], 
      "item_category": "Shirts"
    }, 
    {
      "item_category": "Woolen"
    }
  ], 
  "pages": [
    {
      "date": "24-01-2015", 
      "content": "This is some content about page 1", 
      "name": "Sample Page title 1"
    }, 
    {
      "date": "26-01-2015", 
      "content": "This is some content about page 2", 
      "name": "Sample Page title 2"
    }
  ]
}

我必须在应用程序中创建一个仪表板,该仪表板是根据上述 JSON 数据按以下方式构建的:

Top Menu
=====================
Trimmers (Gridview)

Trimmer1    Trimmer2
Trimmer3    Trimmer4

======================
Shirts (Gridview)

John Players

======================
Pages (Listview)

Page1
Page2

我的仪表板片段获取此 JSON。我的仪表板布局是:

fragment_home.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/relativeLayout1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
</RelativeLayout>

我的仪表板 Activity 看起来像这样:

public class HomeFragment extends Fragment {
   .....
   .....
   @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
      View rootView = inflater.inflate(R.layout.fragment_home, container, false);
      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 posts = catitem.getJSONArray("catalog_items");
                    for(int j=0;j<posts.length();j++){
                        JSONObject postitem = posts.getJSONObject(j);
                        catList.add(postitem.getString("name"));
                    }

                    addGridtoLayout(catitem.getString("item_category"),catList);
                }
            }
        } catch (JSONException e){
            e.printStackTrace();
        }

        return rootView;

}
    public void addGridtoLayout(String title, ArrayList<String> itemList)
        {
            RelativeLayout ll = new RelativeLayout(getActivity());
            RelativeLayout.LayoutParams parambs = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
            ll.setLayoutParams(parambs);


            TextView tv = new TextView(getActivity());
            tv.setText(title);
            tv.setId(R.id.layout1);

            RelativeLayout.LayoutParams lay = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
            ll.addView(tv, lay);

            GridView gridView= new GridView(getActivity());
            gridView.setLayoutParams(new GridView.LayoutParams(GridLayout.LayoutParams.MATCH_PARENT, GridLayout.LayoutParams.MATCH_PARENT));
            gridView.setNumColumns(GRID_COL_NUM);
            gridView.setAdapter(new HomeGridAdapter(getActivity(), itemList));
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
            params.addRule(RelativeLayout.BELOW, tv.getId());
            ll.addView(gridView, params);

            mainLayout.addView(ll);
        }

这是仅用于项目 (Gridview) 的代码。

我要做的基本上是将每个 gridview 和标题包装在一个 relativelayout 中。相关布局将出现在前一个布局下方,从第一项开始。 但是 gridviews 越来越相互堆叠,所有项目都显示在一行中。

有没有什么方法可以定义每个 gridview 的相对布局显示在前一个 gridview 的下方?另外,有没有更好的方法来实现这一目标?我的意思是基本要求是生成动态数量的网格视图。那么动态生成布局是唯一的方法吗?

感谢阅读这么长post。

好吧,正如我在 comment 中提到的,我对代码进行了稍微修改,效果非常好。所以我要详细说明我做了什么。也许它可以帮助某人。

由于我必须生成多个分段的 Gridview,我最初想到使用 TableLayout 生成 tables 并向其添加 TableRows。每个 Table 行将持有一个 RelativeView consiting of:

  1. 项目的图片(来自 JSON)
  2. 项目名称(来自JSON)

所以,我最初创建了这段代码:

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;
  }
...
...
}

这整个代码适用于布局中定义的单个 Table 布局: 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>

所以整个table是即时生成的。工作得很好。我可以将点击处理程序附加到每一行以进行监听,并且我可能必须为每个视图设置标签才能获得点击的项目。然后我想到了更可行和灵活的方法。如果我将 gridview 添加到每一行本身,而不是将 LinearLayouts 附加到每个 Table 行,会怎样?所以,我最终用这种方法修改了我的代码: 1. 单个 Table 行将包含每个类别标题,第二个 Table 行将附加到它,它将包含 gridview。 2. 我现在可以使用自定义网格适配器,通过它我可以更好地更改每个项目的布局,即通过 XML 而不是在这里使用 "on-the-fly" 方法。 3.以上几点也让我获得了点击监听器。所以不需要设置标签。

注意: 上面的方法和下面的方法一样有效(相同)。所以你可以同时使用

因此,我创建了一个将标题添加到每个项目类别的函数,以及一个将项目添加到网格并将网格添加到一行的函数:

HomeFragment.java(gridview版)

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://whosebug.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;
    }
....
}

现在我的自定义适配器 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>

所以我目前使用的是 TableLayout 的 Gridview 版本。我可能已经实现了我想要的,但我真的很欢迎这里的任何建议。希望这个回答对某人有很大的帮助。

谢谢你忍了这么久。