0

I was having some problem when trying to add multiple text views into grid view cell. The number of text views is dynamic whereby it depends on the data in database. Here is how I set up the adapter for grid view:

 adapter = new ArrayAdapter<String>(this.getContext(), android.R.layout.simple_list_item_1, items) {
 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
     LinearLayout ll = (LinearLayout) super.getView(position, convertView, parent);
     
            // for simplicity I hardcoded two text views

            TextView cell = new TextView(this.getContext());
            cell.setText("Test");
            
            TextView cell2 = new TextView(this.getContext());
            cell2.setText("HI");

            ll.addView(cell);
            ll.addView(cell2);

            return ll;
        }
    };

    gridView.setAdapter(adapter);

My .xml file for grid view:

<GridView
    android:id="@+id/gridView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:numColumns="7"
    android:requiresFadingEdge="vertical"
    android:scrollbars="none"
/>

However, I am getting these error messages:

java.lang.ClassCastException: android.support.v7.widget.AppCompatTextView cannot be cast to android.widget.LinearLayout
        at com.mainapp.scheduler.InstrumentSchedulerFragment$3.getView(InstrumentSchedulerFragment.java:586)
        at android.widget.AbsListView.obtainView(AbsListView.java:2365)
        at android.widget.GridView.onMeasure(GridView.java:1065)
        at android.view.View.measure(View.java:22071)
        at android.support.constraint.ConstraintLayout.internalMeasureChildren(ConstraintLayout.java:1212)
        at android.support.constraint.ConstraintLayout.onMeasure(ConstraintLayout.java:1552)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1514)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:806)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:685)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at com.android.internal.policy.DecorView.onMeasure(DecorView.java:724)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2422)
        at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1504)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1761)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1392)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6752)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
        at android.view.Choreographer.doCallbacks(Choreographer.java:723)
        at android.view.Choreographer.doFrame(Choreographer.java:658)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
        at android.os.Handler.handleCallback(Handler.java:790)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

Any ideas why is it so?

enter image description here

Some of the cell requires one text view only. But some others might required 2 to 3 more text views under the days.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
dummygg
  • 233
  • 1
  • 3
  • 12
  • 1
    `simple_list_item_1` is a plain `TextView` (which is substituted with an `AppCompatTextView` when using appcompat). It is not a `LinearLayout`, and so cannot be cast as one. If you want the base item `View` to be a `LinearLayout`, then create your own layout that has a `` as the root, and replace `android.R.layout.simple_list_item_1` with that `R.layout`. – Mike M. Aug 15 '18 at 01:26
  • I would also mention that this design might get a little hairy when dealing with item recycling upon scrolling, so if the possible number of `TextView`s in an item is relatively small, it might be easier to add the maximum number of ``s in the layout, and set their visibility accordingly in the `Adapter`. – Mike M. Aug 15 '18 at 01:30
  • @MikeM. I see I see. But then the number of text views is dynamic in this case. So I should create a list view to hold the text views, then place the list view into the cell of grid view? – dummygg Aug 15 '18 at 01:34
  • No, I meant, for example, if the maximum possible number of `TextView`s is 3, then go ahead and put 3 ``s in your ``, and if an item only needs 2, set the third one's visibility to `GONE` in `getView()`. – Mike M. Aug 15 '18 at 01:37
  • @MikeM. I see I see but do you have any examples I can follow for this case? Is there any way to keep this text view, then append another separate linear layout below it? Because some of the cells requires one text view only. For instance, the one at edited portion. – dummygg Aug 15 '18 at 01:47
  • "do you have any examples I can follow for this case?" – Examples of what, exactly? Creating your own layout? Using the `setVisibility()` method? "Is there any way to keep this text view, then append another separate linear layout below it?" – Why do you need another `LinearLayout`? Put everything you could possibly need for an item in your ``, then just hide the things you don't need for any given item. If an item only needs one `TextView`, then hide everything except that one `TextView`. – Mike M. Aug 15 '18 at 02:00
  • @MikeM. How do I add in empty cells? Because previously I set it to items variable: for (int i = 7; i < totalCell; i++) { items.add(""); } then I assign the list to the adapter. But now I am following this: https://stackoverflow.com/questions/11106418/how-to-set-adapter-in-case-of-multiple-textviews-per-listview and I not sure how to set the empty cell – dummygg Aug 15 '18 at 02:20
  • I really don't understand what you're asking. If `items` is the dataset for the `GridView` and `Adapter`, and you're adding an empty `String` for every data item, does that mean every `GridView` item is supposed to be empty? If not, how are you determining what should go in each `GridView` item if every data item is empty? – Mike M. Aug 15 '18 at 02:31
  • @MikeM. Sorry but my intention is to initialize an empty grid view with 42 cells. Then I loop thru the grid view by using the position variable, get relevant info from the cell, then proceed to pull data from database. If there is any data returned, I proceed to add more text views under that particular cell. That is why the items list is empty at first, then I intended to execute SQL query inside the CustomAdapter class – dummygg Aug 15 '18 at 02:46
  • Nah, you need to get your data together before you pass it to the `Adapter`/`GridView`. A `GridView` is not a normal `ViewGroup`, as it were. It only gets `View`s from the `Adapter` for the items it can currently display, and those are recycled when scrolling. For example, if your `GridView` can only fit 10 items on-screen, then that's about all the `View`s it's going to have as children. If you try to loop over 42 of them, it's going to crash. – Mike M. Aug 15 '18 at 02:56
  • If you are going to load data after creating a GridView with 42 cells, then you need to use temporary fake data and update the cells when the real data comes in. The temporary data could be a list of empty strings. Use an AsyncTask to get the data using a separate thread so you don't slow down the Main thread. When the data comes in have the AsyncTask call to update the list of strings. Make the GridView recreate itself with the new list by calling notifyDataSetChanged() on the ArrayAdapter. – Raymond Mutyaba Aug 15 '18 at 03:26
  • I made an app in a coding interview for https://triplebyte.com which fetched a list of cats (JSONObject with a title, date, description, and image url) from a server. I added the text to a ListView, then fetched the images from the urls and added them to the list when they came in. I created a small white bitmap to take the place of the pictures until they came in. I had 1 main function, 1 custom ArrayAdapter, and 1 custom Cat class. https://github.com/ordinaryname/MeowFest/tree/kitten/app/src/main/java/com/mutyaba/meowfest – Raymond Mutyaba Aug 15 '18 at 03:32

2 Answers2

1

You can check simple_list_item_1.xml and see if the root of it is a TextView. If it is, it will throw a ClassCastException based on your example.

Alex Taylor
  • 8,343
  • 4
  • 25
  • 40
Jack Chen
  • 31
  • 4
0

Create a xml layout (ex. cell_layout.xml) and set it up the way you would want a cell to look like. Use a LinearLayout and place two TextViews inside. Give the two TextViews ids (ex. TextView1, TextView2). When you create the ArrayAdapter use: new ArrayAdapter<>(this, R.layout.cell_layout).

        //Add text for each cell to a string array
        String[] cell0 = new String[] {"Test", "Hi"};
        //Add all cells to an another string array
        String[][] cells = new String[][] {cell0};
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View cellView = convertView;
            if(cellView != null) {
                LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                cellView = layoutInflater.inflate(R.layout.scores, parent);
            }
            cellView.findViewById(R.id.textView1).setText(cells[position][0]);
            cellView.findViewById(R.id.textView2).setText(cells[position][1]);
            return cellView;
        }

        //This is a shortcut. Normally you would create a custom ArrayAdapter and a custom class
Raymond Mutyaba
  • 950
  • 1
  • 9
  • 14
  • I see I see but my intention is to place multiple text views in a cell. Is it doable with some modifications from the code I posted above? Or I have to use some custom adapter – dummygg Aug 15 '18 at 02:34
  • Thanks! But let me double check with you regarding my understanding, basically cell0 contains the list of string I wanted to put in text view. Then what about the cells? – dummygg Aug 15 '18 at 03:44
  • cell0 (also cell1, cell2, cell3 ...) contains a list of strings you would put in a single cell. Each TextView in a cell would be set to one of the strings in that list. cells is a list that would contain all the cells {cell0, cell1, cell2, cell3 ...}. If cell0 is a restaurant menu (restaurantMenu0) it would contain menu items {"pizza", "coca-cola"} and cells would contain a list of restaurants {restaurantMenu0, restaurantMenu1, restaurantMenu2}. – Raymond Mutyaba Aug 15 '18 at 04:19
  • Hey I am getting error message java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference. I created a separate xml file and assign it to LayoutInflater – dummygg Aug 15 '18 at 05:53
  • Change if(cellView != null) { to if(cellView == null) { – Raymond Mutyaba Aug 15 '18 at 06:04
  • I am getting new error message: Caused by: java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView at the layoutInflater.inflate() that line – dummygg Aug 15 '18 at 06:17
  • layoutInflater.inflate(R.layout.scores, parent, false); add false after parent – Raymond Mutyaba Aug 15 '18 at 06:23