【问题标题】:Set up ListView from a String[][]从 String[][] 设置 ListView
【发布时间】:2015-02-10 15:05:00
【问题描述】:

我有一个 String[][] 的数据,我正在尝试从中创建一个自定义 listView

这是数据

String[][] myDataArray = {{"cat1","cat2","cat3","cat4"},
                          {"dog1","dog2","dog3"},
                          {"lion1"},
                          {"monkey1","monkey2"}};

现在这就是我试图在我的listView 中显示这些数据的方式。我希望数组中的每个数组都有自己的行。所以所有的猫都会排成一排(单元格),所有的狗都会排成另一排,依此类推。这是一张图片,可以清楚地说明行中的每个项目,是textView

我制作了cell_4.xmlcell_3.xmlcell_2.xml、cell_1.xml每一行的布局文件。 然后在我试图展示的活动中,我只有一个普通的旧listView

现在我不太确定如何编辑/调整数据。我必须以这种方式显示它。以便它为 String[] 中的每个数组使用正确的单元格布局。我正在考虑使用switch 语句来获取每个内部数组中的项目数。但是ArrayAdapter 遇到了一些麻烦。设置它。

我在 stackoverflow 上查看了几个示例,例如 Custom ListView Android,试图解决这个问题,但无法理解。

编辑

这里正在尝试设置adapter并调用MyListViewAdapter,但我不知道设置为context

代码如下:

private void handleData(String[][] data){
    BaseAdapter adapter = MyListAdapter(context, data);
    ListView list = (ListView) findViewById(R.id.mealsListView);
    list.setAdapter(adapter);

}

【问题讨论】:

    标签: android arrays xml listview


    【解决方案1】:

    一些想法:

    1) 如果你确定要使用ListView,请跳过此点。否则,您可能对原生支持表结构的 GRIDVIEW 感兴趣。

    2) 你的想法是一致的。 ListView 只知道 ROWS,因此您的适配器将被调用以显示 ROW,并且由您决定将该行中的数组转换为具有多个单元格的元素。您将在getView() 中执行此操作

    3) 您将使用项目类型(getViewTypeCountgetItemViewType)来声明您有不同的项目类型。每种类型都是具有给定单元格数量的行:1,2,3,4...

    • 您将覆盖 getViewTypeCount() 以返回一行中的最大单元格数
    • 您可以为静态布局膨胀以获得一行的单元格数量,或者动态生成它

    让我们开始吧...首先在适配器中我们重写 Type 方法来声明 我们的行将是不同的类型:

        @Override
        public int getViewTypeCount() {
    
           return 4; 
           // you have 4 types of rows.
           // SUPER IMPORTANT: No row in the array can have more cells than this number 
           // or getView will crash (you'd have to define additional layouts)
        } 
    
        @Override
        public int getItemViewType(int position) {
    
           // for a given position, you need to return what type is it. This number ranges 
           // from 0 to itemtypecount-1. We return the length of the array (number of cells)
           // this function is called by the View Recycler to appropriately pass you the 
           // correct view to reuse in convertView
    
           return myDataArray[position].length - 1;
        }
    

    然后我们需要实现getView()。典型的实现将是第一个,您创建不同的 XML,第二个是更高级的实现,我们在没有任何 xml 的情况下动态创建布局。

    第一种情况:静态布局

    • 如果您将行数组长度限制为 3 或 4,以避免创建数十个布局,这是理想的选择。因此,您定义了 4 个 xml(即 row_1_childsrow_2_childsrow_3_childsrow_4_childs),它们将是具有该数量子级的行的模板。那么,

    然后在 GetView 中:

    // we define an array of layout ids to quickly select the layout to inflate depending on
    // the number of rows:
    
    private final static int[] sLayouts=new int[] { 
       R.layout.row_1_childs,  
       R.layout.row_2_childs,  
       R.layout.row_3_childs,  
       R.layout.row_4_childs 
    };
    
    
    public View getView (int position, View convertView, ViewGroup parent) {
    
        int maxcells=myDataArray[position].length;
    
        if (convertView == null) {
    
            // generate the appropriate type
    
    
            if (maxcells<=sLayout.length) {
    
                // just check we are in bounds
                convertView=LayoutInflater.from(parent.getContext()).inflate(sLayout[maxcells-1], null);
    
            } else {
    
                // you have a row with too many elements, need to define additional layouts
                throw new RuntimeException ("Need to define more layouts!!");
            }
    
        }
    
        // At this point, convertView is a row of the correct type, either just created,
        // or ready to recycle. Just fill in the cells
        // for example something like this
    
        ViewGroup container=(ViewGroup)convertView;
    
        for (int i=0; i<maxcells; i++) {
    
            // We assume each row is a (linear)layout whose only children are textviews, 
            // one for each cell
            TextView cell=(TextView)container.getChildAt(i); // get textview for cell i
            cell.setText(myDataArray[position][i]);
            cell.setTag( new PositionInfo(position, i)); // we store the cell number and row inside the TextView
            cell.setOnClickListener(mCellClickListener);
    
    
        }
    
        return convertView;
    }
    

    第二种情况:动态布局

    另一种解决方案是动态生成行,并根据需要动态生成尽可能多的文本视图。为此,请继续覆盖 getViewTypeCount() 以返回最大子代数,并像这样定义 getView

    public View getView (int position, View convertView, ViewGroup parent) {
    
        String rowData=myDataArray[position];
    
        if (convertView==null) {
    
           // generate a LinearLayout for number of children:
           LinearLayout row=new LinearLayout(context);
    
           for (int i=0, len=rowData.length(); i<len; i++) {
    
               // generate a textview for each cell
               TextView cell=new TextView(parent.getContext());
    
               // we will use the same clicklistener (very efficient)
               cell.setOnClickListener(mCellClickListener);
    
               row.addView(cell, new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1)); // same width for each cell
           } 
           convertView=row;  
        }
    
        // here convertView has the correct number of children, same as before:
    
        ViewGroup container=(ViewGroup)convertView;
    
        for (int i=0, len=rowData.length(); i<len; i++) {
            TextView cell=(TextView)container.getChildAt(i);
            cell.setText(rowData[i]);
            cell.setTag( new PositionInfo(position, i)); // we store the cell number and row inside the TextView
        }
    
        return convertView;
    
    }
    
    // auxiliar class to store row and col in each textview for the clicklistener
    
    private class PositionInfo {
        public int row, col;
        public PositionInfo(int row, int col) { this.row=row; this.col=col; }
    }
    
    // trick: only one clicklistener for millions of cells
    private View.OnClickListener mCellClickListener=new View.OnClickListener() {
    
       @Override
       public void onClick(View v) {
           PositionInfo position=(PositionInfo)v.getTag(); // we stored this previously
           // you pressed position.row and position.col
       }
    }
    

    解决方案 (1) 很酷,可以手动创建布局并进行大量配置。 解决方案 (2) 很酷,可以以编程方式支持任意数量的单元,以防它们非常不同

    这两种解决方案都非常有效,因为它们与 View 回收器配合得很好:如果您不使用 View Types 并且不断地膨胀布局,您的 ListView 将会滞后并浪费大量内存和资源。强>

    【讨论】:

    • 那么你的解决方案 1 和 2 都使用网格视图吗?我对另一种类型持开放态度...我只需要执行图片中的操作并能够为每个 textView 设置 onClickListerners。对于我的使用,每个单元格最多有 4 个文本视图,并且低至 0。那么最好使用哪个?而且总会有七行,在上图中我有 4 行,但我会有 7 行
    • 不,它们都适用于 Listview。您可以使用其中任何一个,第一个允许您自定义一些布局,但您必须为每个单元格数量定义一个布局。第二个自动生成布局。请参阅我的编辑以分配点击侦听器。
    • 好的,我明白了,但我仍在尝试设置 BaseAdapter?对于我的数据。我有这个BaseAdapter adapter = new MyListAdapter(); 但我给出了编译错误ListElementAdapter (Context, String[][]) in MyListAdapter cannot be applied to ()
    • 看起来您需要将上下文和数据传递给构造函数,不是吗?像 BAseAdapter 适配器 = new MyListAdapter(context, myDataArray)?没有看到你的适配器就无法分辨!无论如何,只要努力让它运行,然后尝试实施我告诉你的,这是一个非常正确的方法,让它运行得非常顺利!
    • 呵呵 .. Context 是一个有效的上下文。您可以从以下位置获取上下文:活动本身(Activity 扩展 Context,因此 "this" 是一个有效的上下文)或任何现有视图,调用 view.getContext();。当您使用findViewById 时,可能您在一个活动中,所以"this" 将是一个有效的上下文
    【解决方案2】:

    您需要通过扩展BaseAdapter 来制作自己的适配器。您可以通过 getView() 方法检查数据的大小,并膨胀正确的布局。

    更新:

    public class MyListAdapter extends BaseAdapter{
        String[][] mData;
        LayoutInflater mLayoutInflater;
    
        public MyListAdapter(Context context, String[][] data) {
            mData = data;
            mLayoutInflater = LayoutInflater.from(context);
        }
    
        @Override
        public int getCount() {
            return mData.length;
        }
    
        @Override
        public Object getItem(int position) {
            return null;
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            String data[] = mData.get(position);
            switch(data.length){
                case 4:
                    convertView = mLayoutInflater.inflate(R.layout.cell_4, parent, false);
                    TextView t1 = (TextView) convertView.findViewById(R.id.one);
                    t1.setText(data[0]);
                    break;
                case 3:
                    convertView = mLayoutInflater.inflate(R.layout.cell_3, parent, false);
                    break;
                case 2:
                    convertView = mLayoutInflater.inflate(R.layout.cell_2, parent, false);
                    break;
                case 1:
                    convertView = mLayoutInflater.inflate(R.layout.cell_1, parent, false);
                    break;
                default:
                    convertView = mLayoutInflater.inflate(R.layout.blank, parent, false);
            }
            return convertView;
        }
    }
    

    【讨论】:

    • 感谢您的回复,您能否展开/显示基本适配器的示例,以及如何开始使用此示例?
    • 这应该让你开始。
    • 好吧,我很接近了,我只是想尝试,但在 public ListElementAdapter 上,我收到编译错误 Invalid declaration: return type retunr type required?
    • 抱歉,没有重命名构造函数
    【解决方案3】:

    如果行中每个字符串的大小不同,您可能会遇到问题,然后您可能必须将数据推送到下一行。 尝试使用替代视图,如果您的目标是对相似数据进行分类,可以考虑使用可扩展的列表视图。

    【讨论】:

      猜你喜欢
      • 2011-12-31
      • 1970-01-01
      • 2014-11-09
      • 2011-01-30
      • 1970-01-01
      • 2011-07-29
      • 1970-01-01
      • 2019-10-12
      • 2012-02-02
      相关资源
      最近更新 更多